/*
 * Certificado.java
 *
 * Created on 14 de noviembre de 2007, 19:19
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package cx.v1.util;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigInteger;
import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Date;
import org.bouncycastle.asn1.x509.X509Extensions;
import org.bouncycastle.asn1.x509.X509Name;
import org.bouncycastle.x509.X509V1CertificateGenerator;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;

public class Certificado {
    
    private Date startCertificado;
    private Date expiryCertificado;
    private BigInteger serialNumber;
    private PrivateKey privateKey;
    private KeyPair keyPair;
    private X509Certificate certificado;
    private String signaturaAlgoritmo;
    private String provider;
    private DNName issuerDN;
    private DNName subjectDN;
    
    public Certificado() {
    }
    
    public Certificado(String certificado) throws Exception
    {
          this.setCertificado(getCertificado(certificado));
    }
    
    public static X509Certificate getCertificado(String certificado) throws Exception
    {
        X509Certificate x509certificate = null;
        try
        {
            InputStream in = new FileInputStream(certificado);
            
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            
            x509certificate = (X509Certificate) certificateFactory.generateCertificate(in);
            
            in.close();
        }
        catch(Exception ex)
        {
            if(ex instanceof FileNotFoundException) {
                throw new Exception("Error: El certificado no fue encontrado");
            }
            else{
                throw new Exception("Error: " + ex.getMessage());
            }
        }
        return x509certificate;
    }
     
    public static PublicKey getClavePublica(String certificado) throws Exception
    {
            X509Certificate x509certificate = getCertificado(certificado);
            PublicKey publicKey = x509certificate.getPublicKey();
            return publicKey;
    }
    
    public PublicKey getClavePublica() throws Exception
    {
            X509Certificate x509certificate = this.getCertificado();
            PublicKey publicKey = x509certificate.getPublicKey();
            return publicKey;
    }
    
    public X509Certificate generateCertificado(Certificado certificado) throws Exception
    {
        try
        {
            X509V1CertificateGenerator genCertificado = new X509V1CertificateGenerator();
            X509Certificate x509Certificado = null;
            if(this.validCertificado(certificado))
            {   
                
                X509Name issuerDN = null; 
                if(this.isNoVacio(certificado.getIssuerDN().getDNName())){
                    issuerDN = new X509Name(certificado.getIssuerDN().getDNName());
                }
                else{
                    throw new Exception("Error: Los datos del Emisor del certificado son obligatorios");
                }
                X509Name subjectDN = null; 
                if(this.isNoVacio(certificado.getSubjectDN().getDNName())){
                    subjectDN = new X509Name(certificado.getSubjectDN().getDNName());          
                }
                else{
                    throw new Exception("Error: Los datos sobre el Asunto de expedici�n del certificado son obligatorios");
                }
                
                genCertificado.setSerialNumber(certificado.getSerialNumber());
                genCertificado.setIssuerDN(issuerDN);
                genCertificado.setNotBefore(certificado.getStartCertificado());
                genCertificado.setNotAfter(certificado.getExpiryCertificado());
                genCertificado.setSubjectDN(subjectDN);
            
                KeyPair keyPair = certificado.getKeyPair();
            
                genCertificado.setPublicKey(keyPair.getPublic());
                genCertificado.setSignatureAlgorithm(certificado.getSignaturaAlgoritmo());
            
                x509Certificado = genCertificado.generate(keyPair.getPrivate(), certificado.getProvider());          
            }
            return x509Certificado;
        }
        catch(Exception ex)
        {
            if(ex instanceof CertificateEncodingException){
                throw new Exception("Error: No existe el proveedor: " + certificado.getProvider());
            }
            else{
                throw new Exception("Error: " + ex.getMessage());
            }
        }
    }
    
    public void createCertificado(byte[] cert, String certificado) throws Exception
    {
        try
        {                
            OutputStream out = new FileOutputStream(certificado);
            
            out.write(cert, 0, cert.length);
            
            out.close();
        }
        catch(Exception ex)
        {
            if(ex instanceof FileNotFoundException){
                throw new Exception("Error: La ruta para grabar el Certificado no existe");
            }
            else{
                throw new Exception("Error: " + ex.getMessage());
            }
        }
    }
    
    public X509Certificate generateCertificado(Certificado certificado, X509Certificate x509Certificado) throws Exception
    {
        try
        {
            if(!this.isNoVacio(x509Certificado)){
                if(this.isNoVacio(this.getCertificado())){
                    x509Certificado = this.getCertificado();
                }
                else{
                    throw new Exception("El certificado padre no existe o no cuenta con la informaci�n suficiente para ser utilizado como certificado padre");
                }
            }
            X509V3CertificateGenerator genCertificado = new X509V3CertificateGenerator();
            X509Certificate xCertificado = null;
            if(this.validCertificado(certificado)){
                X509Name subjectDN = null; 
                if(this.isNoVacio(certificado.getSubjectDN().getDNName())){
                    subjectDN = new X509Name(certificado.getSubjectDN().getDNName());          
                }
                else{
                    throw new Exception("Error: Los datos sobre el Asunto de expedici�n del certificado son obligatorios");
                }
            
                genCertificado.setSerialNumber(certificado.getSerialNumber());
                genCertificado.setIssuerDN(x509Certificado.getSubjectX500Principal());
                genCertificado.setNotBefore(certificado.getStartCertificado());
                genCertificado.setNotAfter(certificado.getExpiryCertificado());
                genCertificado.setSubjectDN(subjectDN);
            
                KeyPair keyPair = certificado.getKeyPair();
            
                genCertificado.setPublicKey(keyPair.getPublic());
                genCertificado.setSignatureAlgorithm(certificado.getSignaturaAlgoritmo());

                genCertificado.addExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(x509Certificado));
                genCertificado.addExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifierStructure(keyPair.getPublic()));

                xCertificado = genCertificado.generate(certificado.getPrivateKey(), certificado.getProvider());
            }
            return xCertificado;
        }
        catch(Exception ex)
        {
           if(ex instanceof CertificateParsingException){
               throw new Exception("Error: El certificado padre utilizado no es correcto");
           }
           else if(ex instanceof CertificateEncodingException){
               throw new Exception("Error: No existe el proveedor: " + certificado.getProvider());
           }
           else{
               throw new Exception("Error: " + ex.getMessage());
           }
        }
    }

    public Date getStartCertificado() {
        return startCertificado;
    }

    public void setStartCertificado(Date startCertificado) {
        this.startCertificado = startCertificado;
    }

    public Date getExpiryCertificado() {
        return expiryCertificado;
    }

    public void setExpiryCertificado(Date expiryCertificado) {
        this.expiryCertificado = expiryCertificado;
    }

    public BigInteger getSerialNumber() {
        return serialNumber;
    }

    public void setSerialNumber(BigInteger serialNumber) {
        this.serialNumber = serialNumber;
    }

    public PrivateKey getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(PrivateKey privateKey) {
        this.privateKey = privateKey;
    }

    public KeyPair getKeyPair() {
        return keyPair;
    }
    
    public void setKeyPair(KeyPair keyPair) {
        this.keyPair = keyPair;
    }

    public X509Certificate getCertificado() {
        return certificado;
    }

    public void setCertificado(X509Certificate certificado) {
        this.certificado = certificado;
    }
    
    public String getSignaturaAlgoritmo() {
        return signaturaAlgoritmo;
    }
    
    public void setSignaturaAlgoritmo(String signaturaAlgoritmo) {
        this.signaturaAlgoritmo = signaturaAlgoritmo;
    }

    public String getProvider() {
        return provider;
    }

    public void setProvider(String provider) {
        this.provider = provider;
    }
    
    private boolean isNoVacio(Object valor)
    {
        if(valor != null){
            return true;
        }
        else{
            return false;
        }
    }
    
    private boolean validCertificado(Certificado certificado) throws Exception
    {
        if(!this.isNoVacio(certificado.getSerialNumber())){
            throw new Exception("Error: El numero de serie del certificado es obligatorio");
        }
        if(!this.isNoVacio(certificado.getStartCertificado())){
            throw new Exception("Error: La fecha de inicio del certificado es obligatoria");
        }
        if(!this.isNoVacio(certificado.getExpiryCertificado())){
            throw new Exception("Error: La fecha de expiraci�n del certificado es obligatoria");
        }
        if(!this.isNoVacio(certificado.getKeyPair())){
            throw new Exception("Error: El contenedor de Claves es obligatorio");
        }
        if(!this.isNoVacio(certificado.getProvider())){
            throw new Exception("Error: EL proveedor del certificado es obligatorio");
        }
        if(!this.isNoVacio(certificado.getSignaturaAlgoritmo())){
            throw new Exception("Error: EL algoritmo de encriptaci�n para el certificado es obligatorio");
        }        
        return true;
    }

    public DNName getIssuerDN() {
        return issuerDN;
    }
    
    public void setIssuerDN(DNName issuerDN) {
        this.issuerDN = issuerDN;
    }

    public DNName getSubjectDN() {
        return subjectDN;
    }

    public void setSubjectDN(DNName subjectDN) {
        this.subjectDN = subjectDN;
    }
}
